// --------------------------------------------------------------------------------------------------------------------
// <copyright file="LiveObjectContainer.cs" company="Open Trader">
//   Copyright (c) David Denis (david.denis@systemathics.com)
// </copyright>
// <summary>
//   |  Open Trader - The Open Source Systematic Trading Platform
//   |
//   |  This program is free software: you can redistribute it and/or modify
//   |  it under the terms of the GNU General Public License as published by
//   |  the Free Software Foundation, either version 2 of the License, or
//   |  (at your option) any later version.
//   |
//   |  This program is distributed in the hope that it will be useful,
//   |  but WITHOUT ANY WARRANTY; without even the implied warranty of
//   |  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//   |  GNU General Public License for more details.
//   |
//   |  You should have received a copy of the GNU General Public License
//   |  along with this program.  If not, see http://www.gnu.org/licenses
//   |
//   |  Up to date informations about Open Trader can be found at :
//   |    http://opentrader.org
//   |    http://opentrader.codeplex.com
//   |
//   |  For professional services, please visit us at :
//   |    http://www.systemathics.com
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Org.OpenTrader.Framework.LiveObjects
{
    #region Using Directives

    #region Using Directives

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Runtime.InteropServices;

    using Org.OpenTrader.Framework.Enums;
    using Org.OpenTrader.Framework.Events;
    using Org.OpenTrader.Framework.LiveObjects.Interfaces;

    #endregion

    #endregion

    /// <summary>
    /// This class is the central container for any instance. It's purpose is to dynamically create/destroy Open Trader objects (ILiveObject)
    /// in an isolated AppDomain
    /// An LiveObjectContainer is created/destroyed using the static methods :
    /// ILiveObjectContainer Create(ILiveObjectContainerDescriptor desc)
    /// void Destroy(ILiveObjectContainer)
    /// The Caller of Create() get a transparent proxy to an ILiveObjectContainer hosted in a freshly created AppDomain (at creation a set of assemblies is given)
    /// The Caller of Destroy() unloads the AppDomain (assemblys, instances and threads will be destroyed)
    /// Keep in mind that any ILiveObject (application, service, plugin, configuration, ...) class inherits from LiveObject
    /// which is RemotingStub, thus using myContainer.CreateLiveObject&lt;IMyType&gt;(...) also returns a IMtyType transparent proxy to the caller
    /// Everything else has to be [Serializable] to be replicated between domains.
    /// With a bit of thinking on interfaces design, only simple and lightweight "by copy" objects have to transit (strings, structs, etc ...)
    /// this ensures that turning from an architecture with n domains inside 1 executable to a multi host .NET remoting scheme is nearly for free
    /// For this particular purpose ILiveObjectDescriptor has an enum for local/remote operations :
    /// - When local is selected the instance is created in some LiveObjectContainer and calls/events are transmitted cross app domain
    /// - When remote is selected, a stub to a remote object is created and calls/events are transmitted over TCP sockets
    /// Notes
    /// LiveObject Container has its private memory and assemby set, thus
    /// - can be unloaded completely
    /// - many versions of assembly distributions can be run at the same time (for example 0.5.0.x for connectivity components and 0.6.0.x for business ones)
    /// - the shutdown ot failure of running instances/threads in a domain may just cause proxy timeout to other domains, not general failure
    /// </summary>
    public sealed class LiveObjectContainer : RemotingStub, ILiveObjectContainer
    {
        private static readonly Identity Identity = Identity.Create(System.Reflection.MethodBase.GetCurrentMethod());

        #region Constants and Fields

        /// <summary>
        /// The allInstancesByGuid.
        /// </summary>
        private readonly IDictionary<Guid, ILiveObject> allInstancesByGuid = new Dictionary<Guid, ILiveObject>();

        /// <summary>
        /// The allInstancesByImplementedInterface.
        /// </summary>
        private readonly IDictionary<string, List<ILiveObject>> allInstancesByImplementedInterface = new Dictionary<string, List<ILiveObject>>();

        /// <summary>
        /// The allInstancesByName.
        /// </summary>
        private readonly IDictionary<string, ILiveObject> allInstancesByName = new Dictionary<string, ILiveObject>();

        /// <summary>
        /// The allLiveobjectClassTypes.
        /// </summary>
        private readonly IDictionary<string, Type> allLiveobjectClassTypes = new Dictionary<string, Type>();

        /// <summary>
        /// The allLiveobjectInterfaceTypes.
        /// </summary>
        private readonly IDictionary<string, Type> allLiveobjectInterfaceTypes = new Dictionary<string, Type>();

        /// <summary>
        /// The allTypes.
        /// </summary>
        private readonly IDictionary<string, Type> allTypes = new Dictionary<string, Type>();

        /// <summary>
        /// The assemblynameByTypename.
        /// </summary>
        private readonly IDictionary<string, string> assemblynameByTypename = new Dictionary<string, string>();

        /// <summary>
        /// The attributes.
        /// </summary>
        private readonly IDictionary<string, IList<Attribute>> attributes = new Dictionary<string, IList<Attribute>>();

        /// <summary>
        /// The interface.
        /// </summary>
        private readonly IDictionary<string, IList<string>> iface = new Dictionary<string, IList<string>>();

        /// <summary>
        /// The implements.
        /// </summary>
        private readonly IDictionary<string, IList<string>> implements = new Dictionary<string, IList<string>>();

        #endregion

        #region Constructors and Destructors

        /// <summary>
        /// Initializes a new instance of the <see cref="LiveObjectContainer"/> class.
        /// </summary>
        public LiveObjectContainer()
        {
            this.LiveObjectsEventCB = new RemotableEventHandler<LiveObjectsEventArgs>("LiveObjectContainer.LiveObjectsEventCB");
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets AllLiveObjects.
        /// </summary>
        public Guid[] AllLiveObjects
        {
            get
            {
                return this.allInstancesByGuid.Keys.ToArray();
            }
        }

        /// <summary>
        /// Gets AllTypes.
        /// </summary>
        public string[] AllTypes
        {
            get
            {
                return this.allLiveobjectClassTypes.Keys.ToArray();
            }
        }

        /// <summary>
        /// Gets or sets Descriptor.
        /// </summary>
        public ILiveObjectContainerDescriptor Descriptor { get; set; }

        /// <summary>
        /// Gets or sets LiveObjectsEventCB.
        /// </summary>
        public RemotableEventHandler<LiveObjectsEventArgs> LiveObjectsEventCB { get; set; }

        #endregion

        #region Public Methods

        /// <summary>
        /// The to string.
        /// </summary>
        /// <returns>
        /// The to string.
        /// </returns>
        public override string ToString()
        {
            return string.Format("{0}", this.Descriptor.Name);
        }

        #endregion

        #region Implemented Interfaces

        #region ILiveObjectContainer

        /// <summary>
        /// The create generics instance.
        /// </summary>
        /// <returns>
        /// The create generics instance.
        /// </returns>
        /// <summary>
        /// The create live object.
        /// </summary>
        /// <param name="instanceDescriptor">
        /// The instanceDescriptor.
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// </returns>
        /// <exception cref="NotImplementedException">
        /// </exception>
        public T CreateLiveObject<T>(ILiveObjectDescriptor instanceDescriptor) where T : ILiveObject
        {
            try
            {
                this.LiveObjectsEventCB.RaiseEvent(this, new LiveObjectsEventArgs("Creating LiveObject " + instanceDescriptor.ID));

                // Reserver some IPC Server Channel
#if ISOLATIONREADY
                string serverChannel = string.Format("OT\\{0}-{1}", Process.GetCurrentProcess().Id, instanceDescriptor.ID.ToString() );
                BinaryServerFormatterSinkProvider serverProvider = new BinaryServerFormatterSinkProvider();
                serverProvider.TypeFilterLevel = System.Runtime.Serialization.Formatters.TypeFilterLevel.Full;
                System.Collections.IDictionary sProperties = new System.Collections.Hashtable();
                sProperties["name"] = serverChannel;
                sProperties["priority"] = "20";
                sProperties["portName"] = serverChannel;
                IChannel Channel2 = new IpcServerChannel(sProperties, serverProvider);
                ChannelServices.RegisterChannel(Channel2);
#endif
                if (instanceDescriptor is LiveObjectDescriptor.Local)
                {
                    // Local descriptor
                    var lid = (LiveObjectDescriptor.Local)instanceDescriptor;

                    // The type wasn't found during introspection phase
                    if (!this.KnowsAboutType(lid.ClassType))
                    {
                        return default(T);
                    }

                    // This is it
                    var type = this.allTypes[lid.ClassType];

                    // If is an interface, not instantiable
                    if (type.IsInterface)
                    {
                        return default(T);
                    }

                    // Use this cosntructor (we're sure it's there, introspection phase kick out those without it)
                    var cinfo = type.GetConstructor(new[] { typeof(ILiveObjectDescriptor), typeof(ILiveObjectContainer) });

                    // Call the wanted constructor
                    var instance = (T)cinfo.Invoke(new object[] { instanceDescriptor, this });
                    ////Stdout.WriteLine("** Calling constructor(descriptor,container) on " + cinfo);

                    // Chain events
                    instance.LiveObjectsEventCB.Chain(this.LiveObjectsEventCB);

                    // Should be ok
                    if (instance != null)
                    {
                        // Register by guid
                        this.allInstancesByGuid[lid.ID] = instance;

                        // Register by name if any
                        if (lid.Name != null)
                        {
                            this.allInstancesByName[lid.Name] = instance;
                        }

                        // Register by implemented interface
                        if (!this.allInstancesByImplementedInterface.ContainsKey(lid.InterfaceType))
                        {
                            this.allInstancesByImplementedInterface[lid.InterfaceType] = new List<ILiveObject>();
                        }

                        this.allInstancesByImplementedInterface[lid.InterfaceType].Add(instance);

                        // Let's go
                        return instance;
                    }
                }
                else if (instanceDescriptor is LiveObjectDescriptor.Remote)
                {
                    // TODO: The remoting client side :-)
                    throw new NotImplementedException();
                }
            }
            catch (Exception e)
            {
                Logging.Log(Identity, ELogLevel.Debug, () => "CreateLiveObject: Failed", e);
                throw;
            }

            return default(T);
        }

        /// <summary>
        /// The create safe enum.
        /// </summary>
        /// <param name="enumFullname">
        /// The enumFullname.
        /// </param>
        /// <param name="enumValue">
        /// The enumValue.
        /// </param>
        /// <summary>
        /// The destroy.
        /// </summary>
        public void Destroy()
        {
        }

        /// <summary>
        /// The attributes.
        /// </summary>
        /// <param name="typeName">
        /// The typeName.
        /// </param>
        /// <returns>
        /// </returns>
        public Attribute[] GetAttributes(string typeName)
        {
            if (this.attributes.ContainsKey(typeName))
            {
                return this.attributes[typeName].ToArray();
            }

            return null;
        }

        /// <summary>
        /// The get configuration schema.
        /// </summary>
        /// <param name="className">
        /// The className.
        /// </param>
        /// <param name="schemaName">
        /// The schemaName.
        /// </param>
        /// <returns>
        /// </returns>
        public IConfigurationSchema GetConfigurationSchema(string className, string schemaName)
        {
            if (! this.attributes.ContainsKey(className))
            {
                return null;
            }

            var attributes = from attribute in this.attributes[className]
                             where attribute is ConfigurationFieldAttribute
                             where (attribute as ConfigurationFieldAttribute).SchemaName.Equals(schemaName)
                             select attribute;

            if (attributes.Count() <= 0)
            {
                return null;
            }

            var v = new ConfigurationSchema(attributes.ToArray());
            return v;
        }

        /// <summary>
        /// The get configuration schema (default one)
        /// </summary>
        /// <param name="className">
        /// The className.
        /// </param>
        /// <returns>
        /// </returns>
        public IConfigurationSchema GetConfigurationSchema(string className)
        {
            return this.GetConfigurationSchema(className, ConfigurationFieldAttribute.DefaultSchema);
        }

        /// <summary>
        /// The find assembly name.
        /// </summary>
        /// <param name="typeName">
        /// The typeName.
        /// </param>
        /// <returns>
        /// The find assembly name.
        /// </returns>
        public string GetContainingAssemblyName(string typeName)
        {
            return this.assemblynameByTypename[typeName];
        }

        /// <summary>
        /// The get enum values.
        /// </summary>
        /// <param name="enumTypeName">
        /// The enum type name.
        /// </param>
        /// <returns>
        /// </returns>
        public string[] GetEnumValues(string enumTypeName)
        {
            if (!this.allTypes.ContainsKey(enumTypeName) || !this.allTypes[enumTypeName].IsEnum)
            {
                return new string[] { };
            }

            IList<string> enumValues = new List<string>();
            foreach (var enumValue in Enum.GetValues(this.allTypes[enumTypeName]))
            {
                enumValues.Add(enumValue.ToString());
            }

            return enumValues.ToArray();
        }

        /// <summary>
        /// The interfaces.
        /// </summary>
        /// <param name="typeName">
        /// The typeName.
        /// </param>
        /// <returns>
        /// </returns>
        public string[] GetImplementedInterfaces(string typeName)
        {
            if (!this.iface.ContainsKey(typeName))
            {
                return null;
            }

            return this.iface[typeName].ToArray();
        }

        /// <summary>
        /// The implementors.
        /// </summary>
        /// <param name="interfaceName">
        /// The interfaceName.
        /// </param>
        /// <returns>
        /// </returns>
        public string[] GetImplementors(string interfaceName)
        {
            if (!this.implements.ContainsKey(interfaceName))
            {
                return new string[] { };
            }

            return this.implements[interfaceName].ToArray();
        }

        /// <summary>
        /// The find live object.
        /// </summary>
        /// <param name="guid">
        /// The guid.
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// </returns>
        public T GetLiveObject<T>(Guid guid) where T : ILiveObject
        {
            if (this.allInstancesByGuid.ContainsKey(guid))
            {
                if (this.allInstancesByGuid[guid] is T)
                {
                    return (T)this.allInstancesByGuid[guid];
                }
            }

            return default(T);
        }

        /// <summary>
        /// The find live object.
        /// </summary>
        /// <param name="name">
        /// The name.
        /// </param>
        /// <typeparam name="T">
        /// </typeparam>
        /// <returns>
        /// </returns>
        public T GetLiveObject<T>(string name) where T : ILiveObject
        {
            if (this.allInstancesByName.ContainsKey(name))
            {
                if (this.allInstancesByName[name] is T)
                {
                    return (T)this.allInstancesByName[name];
                }
            }

            return default(T);
        }

        /// <summary>
        /// The find live objects.
        /// </summary>
        /// <param name="typeName">
        /// The typeName.
        /// </param>
        /// <returns>
        /// </returns>
        public Guid[] GetLiveObjects(string typeName)
        {
            // This type is registered ? If not create a fresh empty list
            if (!this.allInstancesByImplementedInterface.ContainsKey(typeName))
            {
                this.allInstancesByImplementedInterface[typeName] = new List<ILiveObject>();
            }

            var list = new List<Guid>();

            foreach (object o in this.allInstancesByImplementedInterface[typeName])
            {
                list.Add(Guid.NewGuid());
            }

            return list.ToArray();
        }

        /// <summary>
        /// The initialize.
        /// </summary>
        /// <param name="desc">
        /// The desc.
        /// </param>
        public void Initialize(ILiveObjectContainerDescriptor desc)
        {
            // The assembly cache is unique per AppDomain
            Singleton<AssemblyCache>.Instance.Initialize(desc.Type, desc.WorkingDirectory, desc.Resources);

            // This is our descriptor
            this.Descriptor = desc;

            // Statify the AppDomain wide unique container
            Singleton<LiveObjectsRegistry>.Instance.CurrentLiveObjectContainer = this;

            // The registry singleton (useful to set this inside and AppDomain or external Executable)
            if (Singleton<LiveObjectsRegistry>.Instance == null)
            {
                Singleton<LiveObjectsRegistry>.Instance = desc.Registry as LiveObjectsRegistry;
            }

            // Report
            var n = 0;
            Stdout.WriteLine(
                "Assembly Cache for container {0} {1} {2}", this.Descriptor.Name, this.Descriptor.ID, Singleton<AssemblyCache>.Instance.AssemblyNames.Length);
            foreach (var assemblyName in Singleton<AssemblyCache>.Instance.AssemblyNames)
            {
                var assemblyResource = Singleton<AssemblyCache>.Instance[assemblyName];
                if (assemblyResource.Assembly != null)
                {
                    Stdout.WriteLine("Assembly {0} : {1} ({2})", n++, assemblyResource.Assembly.FullName, assemblyResource.GetType().Name);
                }
            }

            // Introspect
            foreach (var assemblyName in Singleton<AssemblyCache>.Instance.AssemblyNames)
            {
                var assemblyResource = Singleton<AssemblyCache>.Instance[assemblyName];
                if (assemblyResource is AssemblyCache.AssemblyResource.Resident || (assemblyResource.Assembly != null && assemblyResource.UseForIntrospection))
                {
                    var r = Singleton<AssemblyCache>.Instance[assemblyName];
                    if (!RuntimeEnvironment.FromGlobalAccessCache(r.Assembly))
                    {
                        foreach (var t in r.Assembly.GetTypes())
                        {
                            this.allTypes[t.FullName] = t;
                        }

                        Stdout.WriteLine(
                            "Introspection of {0} found {1} types to filter LiveObjects from", 
                            new AssemblyName(r.Assembly.FullName).Name, 
                            this.allTypes.Keys.Count);
                    }
                    else
                    {
                        Stdout.WriteLine("Introspection won't happen for {0} (comes from GAC)", r.Assembly.FullName);
                    }
                }
                else
                {
                    // skip
                    continue;
                }
            }

            // Find all classes implementing ILiveObject and keep track of them (they are the only instantiable types for this container)
            var allActivableClassesTypes = this.FilterActivableTypes(this.allTypes.Values.ToArray());
            foreach (var t in allActivableClassesTypes)
            {
                var isApplication = Reflection.ImplementsInterface(t, typeof(IApplication).FullName);
                var isService = Reflection.ImplementsInterface(t, typeof(IService).FullName);
                var isPlugin = Reflection.ImplementsInterface(t, typeof(IPlugin).FullName);

                // i.e: type "Org.Opentrader.SomeClass" is located in assembly "Org.Opentrader.SomeAssembly"
                var aname = new AssemblyName(t.Assembly.FullName);
                this.assemblynameByTypename[t.FullName] = aname.FullName;

                // i.e: type "Org.Opentrader.SomeClass" is t
                this.allLiveobjectClassTypes[t.FullName] = t;

                // i.e: a list of all implemented interfaces (i.e: "thisInterface" -> { thisType , ... , thisOtherType })
                var typeInterfaces = t.GetInterfaces();
                foreach (var typeInterface in typeInterfaces)
                {
                    if (!this.implements.ContainsKey(typeInterface.FullName))
                    {
                        this.implements[typeInterface.FullName] = new List<string>();
                    }

                    this.implements[typeInterface.FullName].Add(t.FullName);
                }

                // If Application or Service or Plugin , we must know what particular inheritor of IApplication, IService or IPlugin is implememented there (ex: IBridgeApplication)
                if (isApplication || isService || isPlugin)
                {
                    this.iface[t.FullName] = new List<string>();

                    var interfaceTypes = Reflection.FindClassMultipleAttributes<ImplementsInterfaceAttribute>(t);
                    foreach (var interfaceType in interfaceTypes)
                    {
                        this.iface[t.FullName].Add(interfaceType.Interface);
                    }
                }

                // Keep track of attributes
                if (!this.attributes.ContainsKey(t.FullName))
                {
                    this.attributes[t.FullName] = new List<Attribute>();
                }

                // Keep track of attributes
                foreach (var v in t.GetCustomAttributes(true))
                {
                    this.attributes[t.FullName].Add(v as Attribute);
                }
            }

            // Find all interfaces deriving ILiveObject
            var allActivableInterfaces = this.FilterActivableInterfaces(this.allTypes.Values.ToArray());
            foreach (var t in allActivableClassesTypes)
            {
                // i.e: type "Org.Opentrader.ISomeInterface" is located in assembly "Org.Opentrader.SomeAssembly"
                var aname = new AssemblyName(t.Assembly.FullName);
                this.assemblynameByTypename[t.FullName] = aname.FullName;

                // i.e: type "Org.Opentrader.ISomeInterface" is  t
                this.allLiveobjectInterfaceTypes[t.FullName] = t;
            }

            // Summary
            Logging.Log(Identity, ELogLevel.Debug, () => "LiveObjectContainer: Summary of known ILiveObject classes");
            foreach (var v in this.assemblynameByTypename.Keys)
            {
                var aname = new AssemblyName(this.assemblynameByTypename[v]);
                Logging.Log(Identity, ELogLevel.Debug, () => string.Format("  {0,-100} in assembly {1,-40}", v, aname.Name));
            }
        }

        /// <summary>
        /// The knows about type.
        /// </summary>
        /// <param name="typeName">
        /// The typeName.
        /// </param>
        /// <returns>
        /// The knows about type.
        /// </returns>
        public bool KnowsAboutType(string typeName)
        {
            return this.assemblynameByTypename.ContainsKey(typeName);
        }

        #endregion

        #endregion

        #region Methods

        /// <summary>
        /// The current domain_ assembly load.
        /// </summary>
        /// <summary>
        /// The filter activable interfaces.
        /// </summary>
        /// <param name="availableTypes">
        /// The availableTypes.
        /// </param>
        /// <returns>
        /// </returns>
        private Type[] FilterActivableInterfaces(Type[] availableTypes)
        {
            var activableInterfaces = new List<Type>();
            foreach (var type in availableTypes)
            {
                // reject reason
                string rejectReason = null;

                // Must be ILiveObject or ISimpleObject
                if (type.GetInterface(typeof(ILiveObject).FullName) == null && type.GetInterface(typeof(ISimpleObject).FullName) == null)
                {
                    rejectReason = "Is not a ILiveObject or ISImpleObject";
                    goto reject;
                }

                // Must be n interface
                if (!type.IsInterface)
                {
                    rejectReason = "Is not an interface";
                    goto reject;
                }

                // Filter done
                goto accept;

                accept:
                Logging.Log(Identity, ELogLevel.Debug, () => string.Format("  Type {0} was accepted", type.FullName));

                // Is an activable type
                activableInterfaces.Add(type);

                reject:

                if (rejectReason != null)
                {
                    // Logger.Log(ELogLevel.Debug, string.Format("  Type {0} was rejected with reason : {1}", type.FullName, rejectReason));
                }
            }

            return activableInterfaces.ToArray();
        }

        /// <summary>
        /// The filter activable types.
        /// </summary>
        /// <param name="availableTypes">
        /// The availableTypes.
        /// </param>
        /// <returns>
        /// </returns>
        private Type[] FilterActivableTypes(Type[] availableTypes)
        {
            var activableTypes = new List<Type>();
            foreach (var type in availableTypes)
            {
                // reject reason
                string rejectReason = null;

                // Accept configuration fields
                if (Reflection.ImplementsInterface(type, typeof(ConfigurationField).FullName))
                {
                    goto accept;
                }

                // Accept schemas
                if (Reflection.ImplementsInterface(type, typeof(IConfigurationSchema).FullName))
                {
                    goto accept;
                }

                // Must be ILiveObject
                if (type.GetInterface(typeof(ILiveObject).FullName) == null && type.GetInterface(typeof(ISimpleObject).FullName) == null)
                {
                    rejectReason = "Is not a ILiveObject ot ISimpleObject";
                    goto reject;
                }

                // Must be a class
                if (!type.IsClass)
                {
                    rejectReason = "Is not a class";
                    goto reject;
                }

                // Must be sealed 
                if (!type.IsSealed)
                {
                    rejectReason = "Is not sealed";
                    goto reject;
                }

                if (type.GetInterface(typeof(ISimpleObject).FullName) != null)
                {
                    goto accept;
                }

                // Must have a "valid" constructor
                if (type.GetConstructor(new[] { typeof(ILiveObjectDescriptor), typeof(ILiveObjectContainer) }) == null)
                {
                    rejectReason = string.Format(
                        "Doesn't define a public constructor \"{0}(ILiveObjectDescriptor desc, ILiveObjectContainer container) {...}\"", type.Name);
                    goto reject;
                }

                // If IConfigurationSchema, no more requirement
                if (type.GetInterface(typeof(IConfigurationSchema).FullName) != null)
                {
                    goto accept;
                }

                if (type.GetInterface(typeof(IApplication).FullName) != null || type.GetInterface(typeof(IService).FullName) != null ||
                    type.GetInterface(typeof(IPlugin).FullName) != null)
                {
                    var interfaceTypes = Reflection.FindClassMultipleAttributes<ImplementsInterfaceAttribute>(type);
                    if (interfaceTypes == null || interfaceTypes.Length <= 0)
                    {
                        rejectReason = "Is in { IApplication, IService, IPlugin } but doesn't have attribute \"ImplementsInterface\"";
                        goto reject;
                    }
                }

                accept:

                // Is an activable type
                Logging.Log(Identity, ELogLevel.Debug, () => string.Format("  Type {0} was accepted", type.FullName));
                activableTypes.Add(type);

                reject:

                // if (rejectReason != null)
                // Logger.Log(ELogLevel.Debug,string.Format("  Type {0} was rejected with reason : {1}", type.FullName, rejectReason));
                continue;
            }

            return activableTypes.ToArray();
        }

        #endregion
    }
}